package com.haoyun.mirage.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.haoyun.common.config.FilterUrlsPropertiesConfig;
import com.haoyun.common.constant.CommonConstant;
import com.haoyun.common.constant.SecurityConstants;
import com.haoyun.common.core.BaseException;
import com.haoyun.common.core.Result;
import com.haoyun.common.core.http.BaseHttpStatus;
import com.haoyun.common.utils.UserUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.xiaoleilu.hutool.collection.CollectionUtil;
import com.xiaoleilu.hutool.http.HttpUtil;
import com.xiaoleilu.hutool.io.IoUtil;
import com.xiaoleilu.hutool.json.JSONObject;
import com.xiaoleilu.hutool.json.JSONUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.DEBUG_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * Created by twg on 2018/4/10.
 */
@Slf4j
@Component
public class AccessTokenFilter extends ZuulFilter {
    private static final String DEFAULT_ERROR_MESSAGE = "未授权，请先登录系统！";

    @Autowired
    private PathMatcher pathMatcher;
    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${security.oauth2.resource.user-info-uri}")
    private String userInfoUri;

    @Autowired
    private FilterUrlsPropertiesConfig filterUrlsPropertiesConfig;

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return DEBUG_FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
        boolean isFilter = true;
        RequestContext context = RequestContext.getCurrentContext();
        for (String url : filterUrlsPropertiesConfig.getAnon()) {
            if (pathMatcher.match(url, context.getRequest().getRequestURI())) {
                isFilter = false;
                break;
            }
        }
        return isFilter;
    }

    @Override
    public Object run() {
        log.info("==========This is AccessTokenFilter =======");
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        HttpServletResponse response = context.getResponse();
        String token = request.getParameter(SecurityConstants.ACCESS_TOKEN);
        log.info("AccessTokenFilter ==== token {}", token);
        if (validateHeaderAccessToken(request) && (StringUtils.isBlank(token) || validateCacheAccessToken(token))) {
            responseOut(response);
        } else {
            try {
                log.info("AccessTokenFilter ==== userInfoUri ======== {} ", userInfoUri);
                JSONObject data = (JSONObject) UserUtil.getUser(request, userInfoUri);
                log.info("用户名：{} IP地址：{} 请求方法：{} URI：{} 参数：{}", UserUtil.getUserName(), HttpUtil.getClientIP(request), request.getMethod(), request.getRequestURI(),JSON.toJSONString(request.getParameterMap()));
                if (data == null) {
                    responseOut(response);
                } else {
                    log.info("AccessTokenFilter ==== JSONObject ======== start ");
                    List<String> roles = Lists.newArrayList();
                    if (data.containsKey("roles")) {
                        roles.addAll(JSONUtil.parseArray(data.get("roles")).toList(String.class));
                    }
                    log.info("AccessTokenFilter ==== JSONObject ======== end ");
                    context.addZuulRequestHeader(SecurityConstants.USER_INFO_HEADER, toStringUserMap(data));
                    log.info("This is addZuulRequestHeader {}", toStringUserMap(data));
                    context.addZuulRequestHeader(SecurityConstants.ROLE_HEADER, CollectionUtil.join(roles, StrUtil.COMMA));
                }
            } catch (BaseException e) {
                log.error("获取用户信息异常，{}", e);
            }
        }
        return null;
    }

    private String toStringUserMap(JSONObject data) {
        Map userMap = Maps.newHashMap();
        userMap.put("id", data.get("id"));
        userMap.put("userName", data.get("userName"));
        userMap.put("compId", data.get("compId"));
        userMap.put("userLevel", data.get("userLevel"));
        return JSON.toJSONString(userMap);
    }

    private void responseOut(HttpServletResponse response) {
        PrintWriter printWriter = null;
        try {
            response.setCharacterEncoding(CommonConstant.UTF8);
            response.setContentType(CommonConstant.CONTENT_TYPE);
            response.setStatus(BaseHttpStatus.SC_UNAUTHORIZED);
            printWriter = response.getWriter();
            printWriter.write(JSON.toJSONString(Result.buildErrorResult(response.getStatus(), DEFAULT_ERROR_MESSAGE)));
        } catch (IOException e) {
        } finally {
            IoUtil.close(printWriter);
        }
    }

    private boolean validateHeaderAccessToken(HttpServletRequest request) {
        String header = request.getHeader(CommonConstant.REQ_HEADER);
        if (header == null || !header.startsWith(CommonConstant.TOKEN_SPLIT)) {
            return true;
        }
        String token = StringUtils.substringAfter(header, CommonConstant.TOKEN_SPLIT);
        if (StrUtil.isBlank(token)) {
            return true;
        }
        return validateCacheAccessToken(token);
    }

    private boolean validateCacheAccessToken(String token) {
        Boolean isExist = redisTemplate.hasKey(String.format("%s%s:%s", SecurityConstants.MIRAGE_PREFIX, "access", token));
        if (!isExist) {
            log.warn("Token：{}，过期或者不存在！", token);
            return true;
        }
        return false;
    }

}
